package com.xugui.thumb.services.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.xugui.thumb.constant.ThumbConstant;
import com.xugui.thumb.manager.cache.CacheManager;
import com.xugui.thumb.mapper.ThumbMapper;
import com.xugui.thumb.pojo.dto.DoThumbRequest;
import com.xugui.thumb.pojo.entity.Blog;
import com.xugui.thumb.pojo.entity.Thumb;
import com.xugui.thumb.pojo.entity.User;
import com.xugui.thumb.services.ThumbService;
import com.xugui.thumb.services.UserService;
import com.xugui.thumb.utils.RedisKeyUtil;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;

/**
 * @author pine
 */
@Service("thumbServiceLocalCache")
@Slf4j
@RequiredArgsConstructor
public class ThumbServiceImpl extends ServiceImpl<ThumbMapper, Thumb> implements ThumbService {

    private final UserService userService;

    private final TransactionTemplate transactionTemplate;

    private final RedisTemplate<String, Object> redisTemplate;

    // 引入缓存管理
    private final CacheManager cacheManager;

    /**
     * 点赞
     * @param doThumbRequest 点赞请求
     * @param request 请求
     * @return 点赞结果
     */
    @Override
    public Boolean doThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {
            throw new RuntimeException("参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        // 加锁
        synchronized (loginUser.getId().toString().intern()) {

            // 编程式事务
            return transactionTemplate.execute(status -> {
                Long blogId = doThumbRequest.getBlogId();  // 博客 ID
                // boolean exists = this.lambdaQuery()
                //         .eq(Thumb::getUserId, loginUser.getId())
                //        .eq(Thumb::getBlogId, blogId)
                //        .exists();

                // 使用 Redis 存储
                Boolean exists = this.hasThumb(blogId, loginUser.getId());
                if (exists) {
                    throw new RuntimeException("用户已点赞");
                }
                // 执行点赞操作  避免循环依赖
                boolean update = Db.lambdaUpdate(Blog.class).eq(Blog::getId, blogId)
                        .setSql("thumb_count = thumb_count + 1")
                        .update();
                // boolean update = blogService.lambdaUpdate()
                //         .eq(Blog::getId, blogId)
                //         .setSql("thumb_count = thumb_count + 1")
                //         .update();

                Thumb thumb = new Thumb();
                thumb.setUserId(loginUser.getId());
                thumb.setBlogId(blogId);
                // 更新成功才执行
                // return update && this.save(thumb);
                boolean success = update && this.save(thumb);
                if (success) {
                    // 需要维护两份数据 将数据添加到 Redis 中
                    // (userId) -> (blogId, thumbId)
                    // redisTemplate.opsForHash().put(
                    //         RedisKeyUtil.getUserThumbKey(loginUser.getId()),
                    //         blogId.toString(),
                    //         thumb.getId());

                    String hashKey = RedisKeyUtil.getUserThumbKey(loginUser.getId());  // thumb:用户Id
                    String fieldKey = blogId.toString();   // 博客ID
                    Long realThumbId = thumb.getId();   // 实际点赞ID
                    // 存入 redis 中 (thumb:用户Id) -> (博客ID, 点赞ID)
                    redisTemplate.opsForHash().put(hashKey, fieldKey, realThumbId);
                    // 添加到本地缓存中
                    cacheManager.putIfPresent(hashKey, fieldKey, realThumbId);
                }
                return success;
            });
        }
    }

    /**
     * 取消点赞
     * @param doThumbRequest 点赞请求
     * @param request 请求
     * @return 取消点赞结果
     */
    @Override
    public Boolean undoThumb(DoThumbRequest doThumbRequest, HttpServletRequest request) {
        if (doThumbRequest == null || doThumbRequest.getBlogId() == null) {
            throw new RuntimeException("参数错误");
        }
        User loginUser = userService.getLoginUser(request);
        // 加锁
        synchronized (loginUser.getId().toString().intern()) {

            // 编程式事务
            return transactionTemplate.execute(status -> {
                Long blogId = doThumbRequest.getBlogId();  // 博客 ID
                // Thumb thumb = this.lambdaQuery()
                //         .eq(Thumb::getUserId, loginUser.getId())
                //         .eq(Thumb::getBlogId, blogId)
                //         .one();

                // (userId) -> (blogId, thumbId)
                // Object thumb = redisTemplate.opsForHash().get(
                //         RedisKeyUtil.getUserThumbKey(loginUser.getId()), blogId.toString());
                // if (thumb == null) {
                //     throw new RuntimeException("用户未点赞");
                // }

                // 获取点赞表中的 ID
                // Long thumbId = Long.valueOf(thumb.toString());

                // 从多级缓存中获取数据
                Object thumbIdObj = cacheManager.get(ThumbConstant.USER_THUMB_KEY_PREFIX + loginUser.getId(), blogId.toString());
                if (thumbIdObj == null || thumbIdObj.equals(ThumbConstant.UN_THUMB_CONSTANT)) {
                    throw new RuntimeException("用户未点赞");
                }

                Long thumbId = Long.valueOf(thumbIdObj.toString());

                // 执行取消点赞的操作 避免循环依赖
                boolean update = Db.lambdaUpdate(Blog.class).eq(Blog::getId, blogId)
                        .setSql("thumb_count = thumb_count - 1")
                        .update();
                // boolean update = blogService.lambdaUpdate()
                //         .eq(Blog::getId, blogId)
                //        .setSql("thumb_count = thumb_count - 1")
                //        .update();
                // return update && this.removeById(thumb.getId());
                boolean success = update && this.removeById(thumbId);

                if (success) {
                    // 需要维护两份数据 将数据从 Redis 中删除
                    // (userId) -> (blogId, thumbId)
                    // redisTemplate.opsForHash().delete(
                    //         RedisKeyUtil.getUserThumbKey(loginUser.getId()), blogId.toString());

                    String hashKey = RedisKeyUtil.getUserThumbKey(loginUser.getId());
                    String fieldKey = blogId.toString();
                    redisTemplate.opsForHash().delete(hashKey, fieldKey);
                    // 将本地缓存中的值设置为 0 表示取消点赞 防止删除热点数据
                    cacheManager.putIfPresent(hashKey, fieldKey, ThumbConstant.UN_THUMB_CONSTANT);
                }
                return success;
            });
        }
    }

    /**
     * 判断用户是否点赞  先查 caffeine 再查 Redis
     * @param blogId 博客 id
     * @param userId 用户 id
     * @return 是否点赞
     */
    @Override
    public Boolean hasThumb(Long blogId, Long userId) {
        // 只需判断 Redis 中是否存在（userId） -> (blogId, thumbId)
        // return redisTemplate.opsForHash().hasKey(
        //        RedisKeyUtil.getUserThumbKey(userId), blogId.toString());

        // return cacheManager.get(RedisKeyUtil.getUserThumbKey(userId), blogId.toString()) != null;

        Object thumbIdObj = cacheManager.get(RedisKeyUtil.getUserThumbKey(userId), blogId.toString());
        if (thumbIdObj == null) {
            return false;
        }
        Long thumbId = (Long) thumbIdObj;
        return !thumbId.equals(ThumbConstant.UN_THUMB_CONSTANT);  // 0L 表示未点赞
    }

}




